package shaka.core

import shaka.core.internal.BeanCategoryImpl
import shaka.core.internal.DateDiffImpl
import shaka.core.internal.StringCategoryImpl
import shaka.core.params.DateDiff
import shaka.core.params.PropArgs

public class DefShakaMethods {
	private static isInit = false
	private static BeanCategoryImpl beanCategory = new BeanCategoryImpl()
	private static DateDiffImpl dateDiffOP = new DateDiffImpl()
	private static StringCategoryImpl stringCategory = new StringCategoryImpl()

	public synchronized static void initialize(){
		try{
			if(isInit)
				return
			// obj as Type
			def oldAsType = Object.metaClass.asType
			Object.metaClass.asType << { Class type ->
				// javaBean as Map
				if(type == Map){
					Map m = delegate.getProperties()
					m.remove('class')
					m.remove('metaClass')
					return m
				}
				return oldAsType.invoke(delegate, type)
			}

			// obj << Map
			Object.metaClass.leftShift << { Map attr ->
				return leftShift(delegate, attr)
			}
			Map.metaClass.leftShift << { Map attr ->
				return leftShift(delegate, attr)
			}

			// obj isEmpty , isNotEmpty , prop
			Object.metaClass.isEmpty << { Object obj ->
				return isEmpty(obj)
			}
			Object.metaClass.isNotEmpty << { Object obj ->
				return !isEmpty(obj)
			}
			Object.metaClass.prop << { Object obj, Object propertyName, Object defaultValue = null ->
				return prop(obj, propertyName, defaultValue)
			}
			Object.metaClass.prop << { Map margs ->
				def args = new PropArgs(isStr:true, trimEmpty:true)
				args << margs

				if(args.obj == null)
					return args.defValue

				if(args.trimEmpty && args.isStr){
					if(args.obj instanceof Map){
						def result = prop(args.obj, args.propName)
						if(result != null && result.class == String)
							return stringCategory.trimEmpty(result, args.defValue)
						else
							return result
					}else{
						MetaProperty p = args.obj.metaClass.getMetaProperty(args.propName)
						if(p.getType() == String){
							return stringCategory.trimEmpty(prop(args.obj, args.propName, args.defValue))
						}
					}
				}
				return prop(args.obj, args.propName, args.defValue)
			}

			// Date operate
			Date.metaClass.plus << { DateDiff diff ->
				return plus(delegate, diff)
			}
			DateDiff.metaClass.plus << { Date date ->
				return plus(date, delegate)
			}
			Date.metaClass.minus << { DateDiff diff ->
				return minus(delegate, diff)
			}
			Date.metaClass.minus << { Date date ->
				return minus(delegate, date)
			}
			String.metaClass.dateFormat << { Date date ->
				return dateFormat(delegate, date)
			}
			String.metaClass.dateParse << { String date ->
				return dateParse(delegate, date)
			}
			Object.metaClass.dateDiff << { Map attr ->
				return dateDiff(attr)
			}
		}finally{
			isInit = true
		}
	}

	/**
	 * 日期運算
	 * <pre>
	 * Date now = 'yyyy-MM-dd HH:mm:ss'.dateParse('2011-10-11 12:30:50')
	 * // key - {@link shaka.core.params.DateDiff}
	 * Date nextYear = now + dateDiff(year : 1)
	 * Date prevYear = now - dateDiff(year : 1)
	 * DateDiff diff = nextYear - prevYear
	 * 
	 * println 'yyyy-MM-dd HH:mm:ss'.dateFormat(nextYear) // 2012-10-11 12:30:50
	 * println 'yyyy-MM-dd HH:mm:ss'.dateFormat(prevYear) // 2010-10-11 12:30:50
	 * println diff as Map // [month:0, day:0, sec:0, hour:0, min:0, year:2]
	 * 
	 * </pre>
	 * @param self default function
	 * @param attr keys {@link shaka.core.params.DateDiff}
	 * @return 日期差物件
	 */
	public static DateDiff dateDiff(Map attr){
		return dateDiffOP.dateDiff(attr)
	}

	/**
	 * 日期運算
	 * @see #dateDiff(Object, Map)
	 * @param patten 日期格式
	 * @param date 日期物件
	 * @return 格式化字串
	 */
	public static String dateFormat(String patten, Date date){
		return dateDiffOP.dateFormat(patten, date)
	}

	/**
	 * 日期運算
	 * @see #dateDiff(Object, Map)
	 * @param pattern 日期格式
	 * @param date 日期字串
	 * @return 日期物件
	 */
	public static Date dateParse(String pattern, String date){
		return dateDiffOP.dateParse(pattern, date)
	}

	/**
	 * 日期運算
	 * @see #dateDiff(Object, Map)
	 * @param date 日期物件
	 * @param diff 日期差物件
	 * @return 計算後的日期
	 */
	public static Date plus(Date date, DateDiff diff){
		return dateDiffOP.plus(date, diff)
	}

	/**
	 * 日期運算
	 * @see #dateDiff(Object, Map)
	 * @param date 日期物件
	 * @param diff 日期差物件
	 * @return 計算後的日期
	 */
	public static Date minus(Date date, DateDiff diff){
		return dateDiffOP.minus(date, diff)
	}

	/**
	 * 日期運算
	 * @see #dateDiff(Object, Map)
	 * @param date1 被減的日期物件
	 * @param date2 要減的日期物件
	 * @return 日期差物件
	 */
	public static DateDiff minus(Date date1, Date date2){
		return dateDiffOP.minus(date1, date2)
	}

	/**
	 * Java Bean & Map
	 * <pre>
	 * 1.leftShift(1)
	 * Bean1 bean1 = new Bean1()
	 * bean1 << [id:1,name:'Shaka']
	 *
	 * 2.leftShift(2)
	 * Bean1 bean1 = Bean1 << [id:1,name:'Shaka']
	 * </pre>
	 *
	 * Map & Map
	 * <pre>
	 * Map m1 = [k1:1,k2:'v2']
	 * println m1 // [k1:1, k2:v2]
	 * m1 << [k1:22]
	 * println m1 // [k1:22, k2:v2]
	 * m1 << null
	 * println m1 // [k1:22, k2:v2]
	 * </pre>
	 *
	 * asType
	 * <pre>
	 * println javaBean as Map
	 * </pre>
	 *
	 * prop - {@link #prop(Object, Object, Object)}
	 * 
	 * @param bean javaBean 或 Map
	 * @param attributes Map
	 * @return java bean 或 Map
	 */
	public static Object leftShift(Object bean, Map attr){
		return beanCategory.bindBean(bean, attr)
	}

	/**
	 * <pre>
	 * 判斷物件是否無內容
	 * 1.null -> true
	 * 2.String : null -> true, ""  -> true
	 * 3.List   : null -> true, []  -> true
	 * 4.Map    : null -> true, [:] -> true
	 * 
	 * 使用
	 * println isEmpty(null)            // true
	 * println isEmpty('')              // true
	 * println isEmpty([])              // true
	 * println isEmpty([1, 2, 3])       // false
	 * println isEmpty([:])             // true
	 * println isEmpty([k1:100,k2:200]) // false
	 * 
	 * PS: isNotEmpty 結果與 isEmpty 相反
	 * 
	 * </pre>
	 * @param obj String 或 Number 或 Map 或 List
	 * @return 是否無內容
	 */
	public static boolean isEmpty(Object obj){
		return beanCategory.isEmpty(obj)
	}

	/**
	 * <pre>
	 * 取出物件的內容, 若沒有內容則以預設值替代
	 * 
	 * 使用1
	 * prop(javaBean_or_map, propName)
	 * prop(javaBean_or_map, propName, defValue)
	 * 
	 * 使用2
	 * prop(obj:javaBean_or_map, propName:'propName', defValue:100, isStr:true, trimEmpty:true)
	 * 
	 * PS:
	 * .obj, propName 為必填項目
	 * .使用 margs 時, 預設 isStr:true, trimEmpty:true
	 * .使用 args 時, 預設 isStr:false, trimEmpty:false
	 * 
	 * </pre>
	 * @param obj javaBean 或 Map 或 List
	 * @param propertyName 屬性名稱 (通常是String,但若 obj為 List 時則為 int)
	 * @param defaultValue 預設值
	 * @return 內容
	 */
	public static Object prop(Object obj, Object propertyName, Object defaultValue){
		return beanCategory.prop(obj, propertyName, defaultValue)
	}
}
